package com.gitee.wsl.io.storage.model

import com.gitee.wsl.io.storage.encoder.Encoder
import kotlinx.serialization.SerializationException
import kotlin.reflect.KType
import kotlin.reflect.typeOf

@Suppress("unused")
class KottageEntry<T : Any> internal constructor(
    private val item: Item,
    private val type: KType,
    private val encoder: Encoder
) {
    val key: String get() = item.key
    val itemType: String get() = item.type
    val createdAt: Long get() = item.createdAt
    val lastReadAt: Long get() = item.lastReadAt
    val expireAt: Long? get() = item.expireAt

    /**
     * @throws ClassCastException casting from raw data failed
     * @throws SerializationException Json decode error. This will occur even if [KottageStorageOptions.ignoreJsonDeserializationError] is true.
     */
    @Throws(
        ClassCastException::class,
        SerializationException::class
    )
    fun get(): T {
        return encoder.decode(item, type)
    }

    fun <T : Any> withType(type: KType): KottageEntry<T> {
        return KottageEntry(
            item = item,
            type = type,
            encoder = encoder
        )
    }

    inline fun <reified T : Any> getWithType(): KottageEntry<T> = withType(type = typeOf<T>())
}


internal data class Item(
    val key: String,
    val type: String,
    val stringValue: String?,
    val longValue: Long?,
    val doubleValue: Double?,
    @Suppress("ArrayInDataClass")
    val bytesValue: ByteArray?,
    val createdAt: Long,
    val lastReadAt: Long,
    val expireAt: Long?
) {
    companion object {
        fun toEntityKey(key: String, type: String): String {
            require(!type.contains("+")) {
                "itemType should not contains \"+\": type = \"$type\""
            }
            return "$type+$key"
        }

        fun keyFromEntityKey(entityKey: String, type: String): String {
            return entityKey.removePrefix("$type+")
        }
    }

    fun getEntityKey(): String {
        return toEntityKey(key, type)
    }

    fun isAvailable(now: Long): Boolean {
        return expireAt?.let { now < it } ?: true
    }

    fun isExpired(now: Long): Boolean {
        return expireAt?.let { it <= now } ?: false
    }
}
